using System;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Text;
using System.Web.Caching;
using System.Web.Hosting;
using System.Linq;

namespace DeployWebLibUtils
{
    /// <summary>
    /// TextVirtualPathProvider is used to serve requests for VirtualFile1.aspx and VirtualFile2.aspx
    /// Any other requests will be redirected to ASP.Net directly without processing
    /// </summary>
    public class EmbeddWebVirtualPathProvider : VirtualPathProvider
    {
        /// <summary>
        /// special method - this is the starting point
        /// </summary>
        public static void AppInitialize()
        {
            HostingEnvironment.RegisterVirtualPathProvider(new EmbeddWebVirtualPathProvider());
        }

        public override bool DirectoryExists(string virtualDir)
        {
            if (virtualDir.StartsWith("/@@"))
            {
                return true;
            }

            return Previous.DirectoryExists(virtualDir);
        }

        public override VirtualDirectory GetDirectory(string virtualDir)
        {
            if (virtualDir.StartsWith("/@@"))
            {
                return new EmbeddWebDir(virtualDir);
            }

            return Previous.GetDirectory(virtualDir);
        }

        public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart)
        {
            if (virtualPath.StartsWith("/@@"))
            {
                return null;
            }

            return Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
        }

        /// <summary>
        /// ASP.Net queries this method to check if the file requested exists or not
        /// </summary>
        /// <param name="virtualPath"></param>
        /// <returns></returns>
        public override bool FileExists(string virtualPath)
        {
            if (virtualPath.StartsWith("/@@"))
            {
                return true;
            }
            
            //if not any of those file, let the Base class handle this
            return Previous.FileExists(virtualPath);
        }

        /// <summary>
        /// ASP.Net requests the virtual file. So we need to give a VirtualFile instance back
        /// </summary>
        /// <param name="virtualPath"></param>
        /// <returns></returns>
        public override VirtualFile GetFile(string virtualPath)
        {
            //if (virtualPath.StartsWith("/@@"))
            if (virtualPath.StartsWith("/@@"))
            {
                return new EmbeddWebFile(virtualPath, this);
            }

            return Previous.GetFile(virtualPath);
        }
    }

    #region class EmbeddWebDir

    public class EmbeddWebDir: VirtualDirectory
    {
        public EmbeddWebDir(string virtualPath): base(virtualPath) {}

        public override IEnumerable Directories
        {
            get
            {
                //return new EmbeddWebDir[0];
                return new IEnumerable[0];
            }
        }

        public override IEnumerable Files
        {
            get
            {
                return new IEnumerable[0];
                //return new EmbeddWebFile[0];
            }
        }

        public override IEnumerable Children
        {
            get
            {
                return new IEnumerable[0];
            }
        }
    }

    #endregion

    #region class EmbeddWebFile

    /// <summary>
    /// This class returns the ASP.Net markup for the requested virtual file.
    /// </summary>
    public class EmbeddWebFile : VirtualFile
    {
        private string _path;
        private EmbeddWebVirtualPathProvider _provider;
        /// <summary>
        /// constructor to initialize the member values of the local instance and of the base class
        /// </summary>
        /// <param name="virtualPath"></param>
        /// <param name="provider"></param>
        public EmbeddWebFile(string virtualPath, EmbeddWebVirtualPathProvider provider)
            : base(virtualPath)
        {
            _path = virtualPath;
            _provider = provider;
        }

        public static MemoryStream ExtractBinaryResource(string assemblyFullname, string resourceFileName)
        {
            Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().SingleOrDefault(ass => ass.FullName == assemblyFullname);

            if (assembly != null)
            {
                Stream stream = assembly.GetManifestResourceStream(resourceFileName);
                return ExtractResourceToStream(stream);
            }

            return null;
        }

        public static string ExtractBinaryResourceToString(string assemblyFullname, string resourceFileName)
        {
            Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().SingleOrDefault(ass => ass.FullName == assemblyFullname);

            if (assembly != null)
            {
                Stream stream = assembly.GetManifestResourceStream(resourceFileName);
                return ExtractResourceToString(stream);
            }

            return null;
        }

        private static string ExtractResourceToString(Stream stream)
        {
            if (stream != null)
            {
                using (stream)
                {
                    using(StreamReader sr = new StreamReader(stream))
                    {
                        return sr.ReadToEnd();
                    }
                }
            }

            return string.Empty;
        }

        private static MemoryStream ExtractResourceToStream(Stream stream)
        {
            if (stream != null)
            {
                using (stream)
                {
                    byte[] buffer = new byte[stream.Length];
                    stream.Read(buffer, 0, (int)stream.Length);
                    return new MemoryStream(buffer);
                }
            }

            return null;
        }

        /// <summary>
        /// This will return the ASP.Net markup in any of the stream format.
        /// </summary>
        /// <returns></returns>
        public override Stream Open()
        {
//            return ExtractBinaryResource("WebControlsLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c647e82a8d43e778",
//                "WebControlsLibrary.MasterPages.Main.Master");

            //            return File.Open(@"d:\myproj\DeployWebLib\Source\Demo.NET35\WebControlsLibrary\MasterPages\Main.Master", FileMode.Open);

            this._path = this._path.Substring(this._path.StartsWith(@"/") ? 3 : 2);
            string[] items = this._path.Split('/');
            string assemblyFullname = string.Format("{0}, Version={1}, Culture={2}, PublicKeyToken={3}",
                items[0], items[1], items[2], items[3]);
            string resourceName = items[4].Replace("_", ".");//.Replace(".aspx", string.Empty);

            if (resourceName.ToLower().EndsWith(".aspx"))
            {
                string fileContent = ExtractBinaryResourceToString(assemblyFullname, resourceName);
                int startIdx = fileContent.IndexOf("MasterPageEmbedType");
                if (fileContent.IndexOf("MasterPageFile") > -1 && startIdx > -1)
                {
                    startIdx = fileContent.IndexOf('"', startIdx);
                    if (startIdx > -1)
                    {
                        int endIdx = fileContent.IndexOf('"', startIdx);
                        string masterPageEmbedType = fileContent.Substring(startIdx + 1, (endIdx - startIdx) - 1);
                        if (!string.IsNullOrEmpty(masterPageEmbedType))
                        {
                            string embeddResourceToken = Type.GetType(masterPageEmbedType).GetEmbeddedControlTypeInfo().EmbeddResourceToken;

                            if (!string.IsNullOrEmpty(embeddResourceToken))
                            {
                                fileContent = fileContent.Remove(startIdx, endIdx - startIdx - 1);
                                fileContent = fileContent.Insert(startIdx, embeddResourceToken);

                                return new MemoryStream(Encoding.UTF8.GetBytes(fileContent));
                            }
                        }
                    }
                }
            }


            return ExtractBinaryResource(assemblyFullname, resourceName);

            //            string str = ExtractBinaryResourceToString(assemblyFullname, resourceName);
            //            string file = File.ReadAllText(@"d:\myproj\DeployWebLib\Source\Demo.NET35\WebControlsLibrary\MasterPages\Main.Master");
            //
            //            Stream stream = new MemoryStream();
            //            StreamWriter writer = new StreamWriter(stream);
            //            writer.Write(str);
            //            writer.Flush();
            //            stream.Seek(0, SeekOrigin.Begin);

            //MemoryStream stream = ExtractBinaryResource(assemblyFullname, resourceName);
            //            return stream;

            //return File.Open(@"d:\myproj\DeployWebLib\Source\Demo.NET35\WebControlsLibrary\MasterPages\Main.Master", FileMode.Open);
            

            //            StreamReader reader = null;
            //            Stream stream = new MemoryStream();
            //
            //            try
            //            {
            //                StreamWriter writer = new StreamWriter(stream);
            //                //if virtual file1, the fetch the virtual file 1
            //                if (_path.EndsWith("/virtualfile1.aspx"))
            //                {
            //                    reader = new StreamReader(HttpContext.Current.Server.MapPath("~/App_Data/VirtualFile1.txt"));
            //                }
            //                else
            //                {
            //                    //else fetch virtual file 2
            //                    reader = new StreamReader(HttpContext.Current.Server.MapPath("~/App_Data/VirtualFile2.txt"));
            //                }
            //
            //                string line;
            //                //read through the file content and replace the markup (the logic here changes for different virtual types)
            //                while ((line = reader.ReadLine()) != null)
            //                    writer.WriteLine(ConvertMarkup(line));
            //
            //                writer.Flush();
            //                stream.Seek(0, SeekOrigin.Begin);
            //                return stream;
            //            }
            //            finally
            //            {
            //                if (reader != null)
            //                    reader.Close();
            //            }
        }
    }

    #endregion
}